package com.ilongge.rank.Utils;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;

import javax.net.ssl.SSLContext;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import com.alibaba.fastjson.JSON;

import cn.hutool.http.HttpRequest;


/**
 * http交互工具类
 * @author Li chang xiao
 * @Date   2021年7月8日 下午4:29:52
 */
@SuppressWarnings("deprecation")
public class HttpClientUtil {

    protected static final Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);

    public static final String METHOD_POST = "POST";

    public static final String METHOD_GET = "GET";

    public static final String DEFAULT_CHARSET = "utf-8";

    public static final String DEFAULT_CONTENT_TYPE = "application/json;charset=UTF-8";

    public static final int DEFAULT_CONNECT_TIMEOUT = 20000;

    public static final int DEFAULT_READ_TIMEOUT = 20000;

    public static final int DEFAULT_CONNECT_REQUEST_TIMEOUT = 20000;

    private static final int MAX_TOTAL = 200;

    private static final int MAX_PER_ROUTE = 50;

    private static final RequestConfig requestConfig;

    private static final PoolingHttpClientConnectionManager connectionManager;

    private static final HttpClientBuilder httpBuilder;

    private static final CloseableHttpClient httpClient;

    private static final CloseableHttpClient httpsClient;


    private static SSLContext sslContext = null;

    static {

        try {
            sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] xcs, String string) {
                    return true;
                }
            }).build();
        } catch (KeyStoreException ex) {
            logger.error(ex.getMessage(), ex);
        } catch (NoSuchAlgorithmException ex) {
            logger.error(ex.getMessage(), ex);
        } catch (KeyManagementException ex) {
            logger.error(ex.getMessage(), ex);
        }
    }

    static {
        String proxy = PropertiesUtil.readProperties("context.properties").getProperty("im.httpclient.proxy");
        String port = PropertiesUtil.readProperties("context.properties").getProperty("im.httpclient.proxy.port");
        logger.info("=====proxyswitch={}    proxyIP={}         proxyPORT={}=======", proxy,port);
        if(StringUtils.isNotEmpty(proxy) && StringUtils.isNotEmpty(port)) {
            HttpHost proxys = new HttpHost(proxy, Integer.valueOf(port), null);
            requestConfig = RequestConfig.custom().setSocketTimeout(DEFAULT_READ_TIMEOUT)
                    .setConnectTimeout(DEFAULT_CONNECT_TIMEOUT).setConnectionRequestTimeout(DEFAULT_CONNECT_REQUEST_TIMEOUT)
                    .setProxy(proxys)
                    .build();
            logger.info("----------------------use proxy method");
        }else {
            requestConfig = RequestConfig.custom().setSocketTimeout(DEFAULT_READ_TIMEOUT)
                    .setConnectTimeout(DEFAULT_CONNECT_TIMEOUT).setConnectionRequestTimeout(DEFAULT_CONNECT_REQUEST_TIMEOUT)
                    .build();
            logger.info("----------------------not use proxy method");
        }

        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", new PlainConnectionSocketFactory())
                .register("https", new SSLConnectionSocketFactory(sslContext,
                        SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER))
                .build();
        connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        connectionManager.setMaxTotal(MAX_TOTAL);
        connectionManager.setDefaultMaxPerRoute(MAX_PER_ROUTE);
        httpBuilder = HttpClientBuilder.create();
        httpBuilder.setDefaultRequestConfig(requestConfig);
        httpBuilder.setConnectionManager(connectionManager);
        httpClient = httpBuilder.build();
        httpsClient = httpBuilder.build();

    }

    private HttpClientUtil() {

    }
    /**
     * 	get请求
     * @param request
     * @param params
     * @throws UnsupportedEncodingException
     */
    public static String get(String url, Map<String, String> headers) {
        HttpGet request = new HttpGet(url);
        String returnStr = "";
        try {
            wrapHeader(request, headers);// 设置请求头
            returnStr = execute(request, httpClient);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            request.releaseConnection();
        }
        logger.info("method get url = {},headers = {} 。\\n result = {} ", url, headers, (StringUtils.isEmpty(returnStr) ? "":(returnStr.length()>500? returnStr.substring(0,400): returnStr)));
        return returnStr;
    }
    /**
     * 	模拟json请求
     * @param request
     * @param params
     * @throws UnsupportedEncodingException
     */
    public static String postBody(String url, String body, Map<String, String> headers) {
        logger.info("headers---->"+headers);
        HttpPost request = new HttpPost(url);
        String returnStr = "";
        try {
            wrapHeader(request, headers);// 设置请求头
            wrapStringEntity(request, body);// 设置body
            returnStr = execute(request, httpClient);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            request.releaseConnection();
        }
        logger.info("method postBody url = {},  body = {}, headers = {} 。\n result = {} ", url, body, headers, (StringUtils.isEmpty(returnStr) ? "":(returnStr.length()>500? returnStr.substring(0,400): returnStr)));
        return returnStr;
    }
    /**
     * 	模拟form请求
     * @param request
     * @param params
     * @throws UnsupportedEncodingException
     */
    public static String postForm(String url, Map<String, String> params, Map<String, String> headers) {
        HttpPost request = new HttpPost(url);
        String returnStr = "";
        try {
            wrapHeader(request, headers);// 设置请求头
            wrapFormEntity(request, params);
            returnStr = execute(request, httpClient);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            request.releaseConnection();
        }
        logger.info("method postForm url = {},  params = {}, headers = {} 。\n result = {} ", url, params, headers, (StringUtils.isEmpty(returnStr) ? "":(returnStr.length()>500? returnStr.substring(0,400): returnStr)));
        return returnStr;
    }
    /**
     * 	模拟form请求无代理
     * @param request
     * @param params
     * @throws UnsupportedEncodingException
     */
    public static String postFormNoPorty(String url, Map<String, String> params, Map<String, String> headers) {
        HttpPost post = new HttpPost(url);
        post.setEntity(getHttpEntity(params));
        String returnStr = getRespString(post, headers);
        logger.info("method postFormNoPorty url = {},  params = {}, headers = {} 。\n result = {} ", url, params, headers, (StringUtils.isEmpty(returnStr) ? "":(returnStr.length()>500? returnStr.substring(0,400): returnStr)));
        return returnStr;
    }
    /**
     * 获取请求体HttpEntity
     *
     * @param params_请求参数
     * @return HttpEntity
     */
    private static HttpEntity getHttpEntity(Map<String, String> params) {
        List<BasicNameValuePair> pairs = new ArrayList<BasicNameValuePair>();
        for (Entry<String, String> entry : params.entrySet()) {
            pairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
        }
        HttpEntity entity = null;
        try {
            entity = new UrlEncodedFormEntity(pairs, "utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return entity;
    }
    /**
     * 执行请求，获取响应内容
     *
     * @param request_请求对象
     * @return 响应内容
     */
    private static String getRespString(HttpUriRequest request, Map<String, String> headers) {
        byte[] bytes = new byte[1024 * 1024];
        int len = 0;
        try (InputStream in = getRespInputStream(request, headers);
             ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
            if (Objects.isNull(in)) {
                return "";
            }
            while ((len = in.read(bytes)) != -1) {
                bos.write(bytes, 0, len);
            }
            return bos.toString("utf-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 执行请求，获取响应流
     *
     * @param request_请求对象
     * @return 响应内容
     */
    private static InputStream getRespInputStream(HttpUriRequest request, Map<String, String> headers) {
        // 设置请求头
        setHeaders(request, headers);
        // 获取响应对象
        HttpResponse response = null;
        try {
            response = HttpClients.createDefault().execute(request);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        // 获取Entity对象
        HttpEntity entity = response.getEntity();
        // 获取响应信息流
        InputStream in = null;
        if (Objects.nonNull(entity)) {
            try {
                in = entity.getContent();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return in;
    }
    /**
     * 设置请求头
     *
     * @param request_请求对象
     * @param headers_请求头参数
     */
    private static void setHeaders(HttpUriRequest request, Map<String, String> headers) {
        if (Objects.nonNull(headers) && !headers.isEmpty()) {
            // 请求头不为空，则设置对应请求头
            for (Entry<String, String> entry : headers.entrySet()) {
                request.setHeader(entry.getKey(), entry.getValue());
            }
        } else {
            // 请求为空时，设置默认请求头
            request.setHeader("Connection", "keep-alive");
            request.setHeader("Accept-Encoding", "gzip, deflate, br");
            request.setHeader("Accept", "*/*");
            request.setHeader("User-Agent",
                    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36");
        }
    }
    /**
     * 	get的https请求
     * @param request
     * @param params
     * @throws UnsupportedEncodingException
     */
    public static String getHttps(String url, Map<String, String> headers) {
        HttpGet request = new HttpGet(url);
        String returnStr = "";
        try {
            wrapHeader(request, headers);// 设置请求头
            returnStr = execute(request, httpsClient);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            request.releaseConnection();
        }
        logger.info("method getHttps url = {}, headers = {} 。\\n result = {} ", url, headers, (StringUtils.isEmpty(returnStr) ? "":(returnStr.length()>500? returnStr.substring(0,400): returnStr)));
        return returnStr;
    }

    /**
     * 	模拟json的https请求
     * @param request
     * @param params
     * @throws UnsupportedEncodingException
     */
    public static String postBodyHttps(String url, String body, Map<String, String> headers) {
        HttpPost request = new HttpPost(url);
        String returnStr = "";
        try {
            wrapHeader(request, headers);// 设置请求头
            wrapStringEntity(request, body);// 设置body
            returnStr = execute(request, httpsClient);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            request.releaseConnection();
        }
        logger.info("method postBodyHttps url = {},  body = {}, headers = {} 。\\n result = {} ", url, body, headers, (StringUtils.isEmpty(returnStr) ? "":(returnStr.length()>500? returnStr.substring(0,400): returnStr)));
        return returnStr;
    }

    /**
     * 	模拟json的https请求
     * @param url
     * @param body
     * @throws UnsupportedEncodingException
     */
    public static String postBodyHttpsNoPorty(String url, String body, Map<String, String> headers) {

        HttpRequest httpRequest = HttpRequest.post(url)
                .setConnectionTimeout(3000)
                .headerMap(headers,true);

        String returnStr = httpRequest.execute().body();
        logger.info("method postBodyHttps url = {},  body = {}, headers = {} 。\\n result = {} ", url, body, headers, (StringUtils.isEmpty(returnStr) ? "":(returnStr.length()>500? returnStr.substring(0,400): returnStr)));
        return returnStr;
    }
    /**
     * 	模拟json的https请求
     * @param url
     * @param body
     * @throws UnsupportedEncodingException
     */
    public static String postJsonHttps(String url, String body) {
        HttpPost request = new HttpPost(url);
        String returnStr = "";
        Map<String, String> headers = new HashMap<String, String>();
        try {
            headers.put("Content-Type", "application/json");
            wrapHeader(request, headers);// 设置请求头
            wrapStringEntity(request, body);// 设置body
            returnStr = execute(request, httpsClient);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            request.releaseConnection();
        }
        logger.info("method postBodyHttps url = {},  body = {}, headers = {} 。\\n result = {} ", url, body, headers, (StringUtils.isEmpty(returnStr) ? "":(returnStr.length()>500? returnStr.substring(0,400): returnStr)));
        return returnStr;
    }

    /**
     * 	模拟form表单的https请求
     * @param url
     * @param params
     * @throws UnsupportedEncodingException
     */
    public static String postFormHttps(String url, Map<String, String> params, Map<String, String> headers) {
        HttpPost request = new HttpPost(url);
        String returnStr = "";
        try {
            wrapHeader(request, headers);// 设置请求头
            wrapFormEntity(request, params);
            returnStr = execute(request, httpsClient);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        } finally {
            request.releaseConnection();
        }
        logger.info("method postFormHttps url = {},  params = {}, headers = {} 。\n result = {} ", url, params, headers, (StringUtils.isEmpty(returnStr) ? "":(returnStr.length()>500? returnStr.substring(0,400): returnStr)));
        return returnStr;
    }

    /**
     * 	统一执行执行请求方法
     * @param request
     * @param httpClient
     * @return
     */
    private static String execute(HttpRequestBase request, CloseableHttpClient httpClient) {
        String respJson = null;
        try (CloseableHttpResponse response = httpClient.execute(request);) {
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                HttpEntity httpEntity = response.getEntity();
                respJson = EntityUtils.toString(httpEntity, DEFAULT_CHARSET);
                EntityUtils.consume(httpEntity);
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        return respJson;
    }
    /**
     * 	设置请求头参数
     * @param request
     * @param params
     * @throws UnsupportedEncodingException
     */
    private static void wrapHeader(HttpRequestBase request, Map<String, String> headers) {
        // 设置请求头
        if (null != headers) {
            for (Entry<String, String> entry : headers.entrySet()) {
                request.addHeader(entry.getKey(), entry.getValue());
            }
        }
    }
    /**
     * 	模拟json请求，设置参数
     * @param request
     * @param params
     * @throws UnsupportedEncodingException
     */
    private static void wrapStringEntity(HttpPost request, String body) {
        // 设置body
        if (body != null) {
            StringEntity entity = new StringEntity(body, DEFAULT_CHARSET);// 解决中文乱码问题
            entity.setContentEncoding(DEFAULT_CHARSET);
            request.setEntity(entity);
        }
    }

    /**
     * 	模拟form请求，设置参数
     * @param request
     * @param params
     * @throws UnsupportedEncodingException
     */
    private static void wrapFormEntity(HttpPost request, Map<String, String> params)
            throws UnsupportedEncodingException {
        if (params != null) {
            List<NameValuePair> nvps = new ArrayList<>();
            for (Entry<String, String> entry : params.entrySet()) {
                nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
            }
            request.setEntity(new UrlEncodedFormEntity(nvps, DEFAULT_CHARSET));
        }
    }

    public static InputStream smallProgramHttpClient(String url, Map<String,Object> data) throws Exception {
        CloseableHttpClient httpClient = httpsClient;
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");
        String body = JSON.toJSONString(data);
        StringEntity entity = null;
        entity = new StringEntity(body);
        entity.setContentType("image/png");
        httpPost.setEntity(entity);
        HttpResponse response = httpClient.execute(httpPost);
        InputStream inputStream = response.getEntity().getContent();
        logger.info("method postBodyHttps url = {},  body = {}, result = {} ", url, body, inputStream.available());
        return inputStream;
    }


    /**
     * form提交
     */
    public static String doHttpFromPost(String url,Map<String,Object> params) {
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap<String,Object> form = new LinkedMultiValueMap<String,Object>();
        if(params!=null) {
            Iterator<String> iter = params.keySet().iterator();
            while(iter.hasNext()) {
                String key = iter.next();
                form.add(key, params.get(key));
            }
        }
        org.springframework.http.HttpEntity<MultiValueMap<String,Object>> forms =
                new org.springframework.http.HttpEntity<>(form,headers);
        String res = restTemplate.postForObject(url, forms, String.class);
        logger.info("===========doHttpFromPost:url={},res={}",url,res);
        return res;
    }

    /**
     * form提交
     */
    public static String doHttpFromPost2(String url,Map<String,Object> params, Map<String, String> header) {
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.set("AppKey", header.get("AppKey"));
        headers.set("nonce", header.get("nonce"));
        headers.set("curTime", header.get("curTime"));
        headers.set("checkSum", header.get("checkSum"));
        MultiValueMap<String,Object> form = new LinkedMultiValueMap<String,Object>();
        if(params!=null) {
            Iterator<String> iter = params.keySet().iterator();
            while(iter.hasNext()) {
                String key = iter.next();
                form.add(key, params.get(key));
            }
        }
        org.springframework.http.HttpEntity<MultiValueMap<String,Object>> forms =
                new org.springframework.http.HttpEntity<>(form,headers);
        String returnStr = restTemplate.postForObject(url, forms, String.class);
        logger.info("===========doHttpFromPost:url={},res={}",url,(StringUtils.isEmpty(returnStr) ? "":(returnStr.length()>500? returnStr.substring(0,400): returnStr)));
        return returnStr;
    }


}
